package org.desktop.diskutils.business.files.impl;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.desktop.diskutils.business.categories.IFileCategory;
import org.desktop.diskutils.business.categories.IFileCategoryEntity;
import org.desktop.diskutils.business.files.IFileTree;
import org.desktop.diskutils.business.files.IFileTreeNode;
import org.desktop.diskutils.business.files.ITreeNodeVisitor;

/**
 * 
 * @author nikolay.grozev
 * 
 */
public class FileTree implements IFileTree {

	private static final double MAX_DISPLAY_SUMMARY_RATIO = 0.005;

	private FileTreeNode root;
	private String originationAddress;
	private IFileCategory rootCategory;

	public FileTree(final String originationAddress) {
		this(originationAddress, null);
	}

	public FileTree(final String originationAddress, final FileTreeNode root) {
		this(originationAddress, root, null);
	}
	
	public FileTree(final String originationAddress, final FileTreeNode root, final IFileCategory rootCategory) {
		setOriginationAddress(originationAddress);
		setRoot(root);
		setRootCategory(rootCategory);
	}

	@Override
	public FileTreeNode getRoot() {
		return root;
	}

	public final void setRoot(final FileTreeNode root) {
		this.root = root;
		root.setTree(this);
	}

	@Override
	public String getOriginationAddress() {
		return originationAddress;
	}

	public final void setOriginationAddress(String originationAddress) {
		this.originationAddress = originationAddress;
	}

	@Override
	public IFileCategory getRootCategory() {
		return rootCategory;
	}

	public void setRootCategory(IFileCategory rootCategory) {
		this.rootCategory = rootCategory;
	}

	public void summarizeDisplayData(final double levelOfSummary) {
		final double summaryAreaRatio = MAX_DISPLAY_SUMMARY_RATIO * levelOfSummary;
		getRoot().visitTreeStructure(new ITreeNodeVisitor() {

			private FileTreeNode currentNodeToSummarize;
			private Map<IFileCategoryEntity, Long> summaryData = new LinkedHashMap<>();

			@Override
			public void preVisitBranchNode(final IFileTreeNode branchNode) {
				if (currentNodeToSummarize == null && branchNode.getSize() < getRoot().getSize() * summaryAreaRatio) {
					currentNodeToSummarize = (FileTreeNode) branchNode;
					summaryData.clear();
				}
			}

			@Override
			public void visit(final IFileTreeNode node) {
				if (currentNodeToSummarize != null && node.getChildren().isEmpty()) {
					IFileCategoryEntity entity = getRootCategory().getEntryFor(node.getCategoryName(), levelOfSummary);
					if (!summaryData.containsKey(entity)) {
						summaryData.put(entity, node.getSize());
					} else {
						summaryData.put(entity, summaryData.get(entity));
					}
				}
			}

			@Override
			public void postVisitBranchNode(final IFileTreeNode branchNode) {
				if (branchNode.equals(currentNodeToSummarize)) {
					Set<FileTreeNode> newDisplayNodes = new LinkedHashSet<>();
					for (Map.Entry<IFileCategoryEntity, Long> entry : summaryData.entrySet()) {
						FileTreeNode treeNode = new FileTreeNode(entry.getKey().getName(), entry.getValue(),
								FileTree.this);
						newDisplayNodes.add(treeNode);
						treeNode.setCategoryName(entry.getKey().getName());
					}
					currentNodeToSummarize.setDisplayChildren(newDisplayNodes);
					
					currentNodeToSummarize = null;
					summaryData.clear();
				}
			}
		});
	}
}
